package balance

import (
	"errors"
	"math"
	"net/http"
	"reflect"
	"sync"
)

type Weighter interface {
	Weight() int
}

type FailTimer interface {
	FailTimes() int
}

type UIDer interface {
	UID() int
}

// statisticsInfo 用于均衡计算的部分数据整合
type statisticsInfo struct {
	robinIndex int        // 轮询的下标
	Len        int        // Instances的数量
	WeightPool []int      // 权重信息
	Lock       sync.Mutex // 均衡计算的锁
	ConnInfo   []uint64   // 连接信息,统计已转发多少请求
	connMutex  sync.Mutex

	RandSeek int64 // 随机种子，用来和纳秒结合，防止种子重复
}

// load_balance service 结构体
type Service struct {
	once        sync.Once     // 用来初始化 用于均衡计算的部分属性
	Instances   []interface{} // 实例 编号-upclient实例
	ServiceName string        // 策略方式 round 轮询 random 随机访问 weight 权重随机 ip_hash IP散列

	Lock      sync.Mutex
	CountInfo statisticsInfo // 统计信息, 后端服务如果有变动，那么统计信息重新计算

}

func NewService(name string) (service *Service, err error) {
	service = &Service{
		CountInfo:   statisticsInfo{},
		ServiceName: name,
		Instances:   []interface{}{},
	}
	return
}

func NewServiceWithInstance(typ string, inst interface{}) (service *Service, err error) {
	service = &Service{
		CountInfo:   statisticsInfo{},
		ServiceName: typ,
		Instances:   []interface{}{},//instance group
	}
	service.Instances = append(service.Instances,inst)
	return
}

// AddInstance添加后端实例
func (service *Service) AddInstance(inst interface{}) bool {
	service.Lock.Lock()
	defer service.Lock.Unlock()
	for _, instance := range service.Instances {
		if reflect.DeepEqual(instance, inst) {
			return false
		}
	}
	service.Instances = append(service.Instances, inst)
	service.GenerateStaticInfo()
	return true
}

// RemoveInstance 后端不可用的时候，从负载均衡器中剔除掉
func (service *Service) RemoveInstance(inst interface{}) bool {
	if len(service.Instances) == 0 {
		return false
	}

	service.Lock.Lock()
	defer service.Lock.Unlock()
	var index = -1
	for i, instance := range service.Instances {
		if reflect.DeepEqual(instance, inst) {
			index = i
		}
	}
	if index < 0 {
		return false
	}

	if index+1 == len(service.Instances) { // 如果是最后一个
		service.Instances = service.Instances[:index]
	} else {
		service.Instances = append(service.Instances[0:index], service.Instances[index+1:]...)
	}
	service.GenerateStaticInfo()
	return false
}

// GenerateStaticInfo 初始化统计信息  后端服务有变化的时候，同步
func (service *Service) GenerateStaticInfo() (err error) {
	service.CountInfo.Lock.Lock()
	defer service.CountInfo.Lock.Unlock()

	service.CountInfo.Len = len(service.Instances)
	service.CountInfo.WeightPool = []int{} // 如果之前有的话，清空, 没有的重新声明
	var weightInfo = make(map[int]int, 8)
	for _, instance := range service.Instances {
		weightInfo[instance.(UIDer).UID()-1] = instance.(Weighter).Weight()
	}
	Percent(weightInfo)
	for index, weight := range weightInfo {
		for j := 0; j < weight; j++ {
			service.CountInfo.WeightPool = append(service.CountInfo.WeightPool, index)
		}
	}
	return
}

// DoBalance 负载均衡
func (service *Service) DoBalance(req *http.Request) (instIndex int, err error) {
	if len(service.Instances) == 0 {
		err = errors.New("No Upstreams Available")
		return
	}
	switch service.ServiceName {
	case "round":	//轮训
		instIndex, err = service.roundBalance(req)
	case "weight":	//权重
		instIndex, err = service.weightBalance(req)
	case "ip_hash":	//hash
		instIndex, err = service.ipHashBalance(req)
	case "random":	//随机
		instIndex, err = service.randomBalance(req)
	default:
		err = errors.New(service.ServiceName + "负载策略不被支持")
	}

	return
}

// Percent 百分比value
func Percent(data map[int]int) {
	total := 0
	for _, v := range data {
		total += v
	}
	//如果data只有一个元素
	if len(data) == 1 {
		for key, _ := range data {
			data[key] = 100
		}
		return
	}

	var last int
	var calTotal int
	for key, v := range data {
		data[key] = int(math.Floor(float64(v*1000)/float64(total))) / 10
		calTotal += data[key]
		last = key
	}

	diff := calTotal - 100
	if diff != 0 {
		data[last] = data[last] - diff
	}
	return
}
